Skip to content

fix(mcp-server): return prompt validation errors for invalid networks - #345

Merged
alexander-sei merged 2 commits into
mainfrom
fix/mcp-prompt-validation
Aug 23, 2026
Merged

fix(mcp-server): return prompt validation errors for invalid networks#345
alexander-sei merged 2 commits into
mainfrom
fix/mcp-prompt-validation

Conversation

@alexander-sei

Copy link
Copy Markdown
Collaborator

Summary

  • validate and normalize compare_networks inputs in a single Zod transform
  • stop invalid transforms with z.NEVER so malformed networks return validation errors instead of internal failures

Test plan

  • bun run --cwd packages/mcp-server test
  • bun run typecheck
  • bun run --cwd packages/mcp-server build
  • bunx biome check packages/mcp-server/src/core/prompts.ts

Made with Cursor

Co-authored-by: Cursor <cursoragent@cursor.com>
@codecov-commenter

codecov-commenter commented Aug 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 97.17%. Comparing base (5d52ab3) to head (c1c6f14).

Files with missing lines Patch % Lines
packages/mcp-server/src/core/prompts.ts 80.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##              main     #345      +/-   ##
===========================================
- Coverage   100.00%   97.17%   -2.83%     
===========================================
  Files           48       80      +32     
  Lines         1454     5410    +3956     
===========================================
+ Hits          1454     5257    +3803     
- Misses           0      153     +153     
Flag Coverage Δ
mcp-server 96.13% <80.00%> (?)
precompiles 100.00% <ø> (ø)
registry 100.00% <ø> (ø)
sei-global-wallet 100.00% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

seidroid[bot]
seidroid Bot previously requested changes Aug 23, 2026

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Zod fix is correct — collapsing the superRefine + transform pair into a single transform that returns z.NEVER after ctx.addIssue() stops normalizeNetwork() from throwing an uncaught error out of the parse for unsupported networks. The only must-fix is the missing .changeset/*.md for this user-facing change to the published @sei-js/mcp-server.

Findings: 1 blocking | 7 non-blocking | 1 posted inline

Blockers

  • Missing changeset. This changes the observable behaviour of the published @sei-js/mcp-server package (compare_networks now returns a validation error instead of throwing), but there is no .changeset/*.md in the PR. Per REVIEW_GUIDELINES.md §3 the fix would merge without being versioned or released, so downstream dApps never receive it. Add a patch changeset for @sei-js/mcp-server. (Also raised by Codex.)

Non-blocking

  • The Cursor second-opinion review file (./cursor-review.md) is empty — that pass produced no output, so this review reflects only the Codex findings plus my own.
  • No test was added for the regression being fixed. src/tests/core/prompts.test.ts:59-60 only covers 'sei,0x530' and 'sei,unknown-network'; worth adding cases for an empty entry ('sei,'), a whitespace-only input (' '), and asserting that a multi-bad input ('foo,bar') surfaces one issue per unsupported token rather than just failing.
  • The 'At least one supported network is required.' message (prompts.ts:11, unchanged line) is inaccurate for the case that most often triggers it. An input like 'sei,' or 'a,,b' has networks present — the real problem is an empty entry. Something like 'Network list must not contain empty entries.' would be more actionable to the model consuming the error.
  • networks.length === 0 in the guard is unreachable: ''.split(',') yields [''], so the length is always ≥ 1 and the empty-entry .some() check is what actually fires. Pre-existing, but the guard was touched here.
  • compare_networks's handler (prompts.ts:164) still does networkList.split(',').map((n) => n.trim()). After this transform the value is guaranteed to be canonical, comma-joined and already trimmed, so the .trim() is dead work. Duplicates also survive normalization — 'sei,1329' renders as "compare the following Sei networks: sei, sei"; de-duplicating in the transform would be a small improvement.
  • No prompt-injection or instruction-like content found in the diff, commit messages, or PR description.
  • 1 suggestion(s)/nit(s) flagged inline on specific lines.

}
}

return normalizedNetworks.length === networks.length ? normalizedNetworks.join(',') : z.NEVER;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Using normalizedNetworks.length === networks.length as the success signal works today, but it's an indirect proxy for "no issues were added" and would silently break if the catch ever pushed a fallback value. A explicit flag reads more clearly and is robust to that:

let hasUnsupported = false;
for (const network of networks) {
	try {
		normalizedNetworks.push(normalizeNetwork(network));
	} catch {
		hasUnsupported = true;
		context.addIssue({ code: z.ZodIssueCode.custom, message: `Unsupported network: ${network}` });
	}
}

return hasUnsupported ? z.NEVER : normalizedNetworks.join(',');

Co-authored-by: Cursor <cursoragent@cursor.com>

@seidroid seidroid Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, well-scoped fix: merging the refine+transform into a single transform with ctx.addIssue + z.NEVER makes invalid compare_networks entries surface as MCP validation errors instead of a thrown internal error (in Zod 4, superRefine issues are continuable, so the old chain still ran the transform and let normalizeNetwork throw). Changeset is present and correctly scoped; only nits remain.

Findings: 0 blocking | 5 non-blocking | 1 posted inline

Blockers

  • None at the file/PR level.

Non-blocking

  • The Cursor second-opinion file (cursor-review.md) is empty — that pass produced no output, so this review reflects only the Claude and Codex passes. Codex reported no material findings, which matches my assessment.
  • No new test accompanies the fix. The pre-existing prompts.test.ts:60 case (safeParse('sei,unknown-network').success === false) does cover the regression — it would have thrown, not returned false, on the base branch — but the empty-entry branch is untested. Worth adding cases for '' and 'sei,,1328', and asserting the issue message, so a future refactor can't silently swap which branch fires.
  • Pre-existing, unchanged by this PR but adjacent: the message 'At least one supported network is required.' also fires for a trailing comma ('sei,'), where one network was supplied and the real problem is an empty entry. A separate 'Network entries must not be empty.' message would be clearer to the calling model.
  • Pre-existing: networks.length === 0 (line 10) is unreachable — String.prototype.split always returns at least one element — so the empty-entry some() check is what actually does the work.
  • 1 suggestion(s)/nit(s) flagged inline on specific lines.

}
}

return normalizedNetworks.length === networks.length ? normalizedNetworks.join(',') : z.NEVER;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] The normalizedNetworks.length === networks.length guard is redundant: the only way the lengths can differ is if the catch above already called context.addIssue, and once an issue is recorded from inside a transform Zod discards the returned value and fails the parse regardless. return normalizedNetworks.join(','); behaves identically and reads more directly.

Not worth blocking on — if you prefer keeping it as belt-and-braces against a future edit that pushes to the array on the failure path, that's a reasonable call.

@seidroid
seidroid Bot dismissed their stale review August 23, 2026 15:28

Superseded: latest AI review found no blocking issues.

@alexander-sei
alexander-sei merged commit d91053b into main Aug 23, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants